#include "ArchivoAuxiliar.h"

ArchivoAuxiliar::ArchivoAuxiliar(const char* filename):Archivo((string(Archivo::indexDir)).append(filename).c_str()){
    this->filename = (string(Archivo::indexDir)).append(filename).c_str();

    string strDocs = string("");
    string strPos = string("");
    string strTerm = string("");

    if ((string(filename)).compare("IndiceInvertido") == 0) {
        strDocs = (string(Archivo::indexDir)).append("Documentos");
        strPos = (string(Archivo::indexDir)).append("Posiciones");
        strTerm = (string(Archivo::indexDir)).append("Terminos");
    }

    else if ((string(filename)).compare("IndiceInvertidoAux") == 0) {
        strDocs = (string(Archivo::indexDir)).append("DocumentosAux");
        strPos = (string(Archivo::indexDir)).append("PosicionesAux");
        strTerm = (string(Archivo::indexDir)).append("TerminosAux");
    }

    this->docs = new ArchivoComprimido(strDocs.c_str());
    this->pos = new ArchivoComprimido(strPos.c_str());

    this->term = new ArchivoTerminos(strTerm.c_str());
    //this->regSig.numTerm = 0;
    this->o_PosAnterior = 0;

    this->clear();
    this->seekg(0, ios::end);
    this->length = this->tellg();

    this->clear();
    this->seekg(0, ios::beg);
}

void ArchivoAuxiliar::write(RegistroAuxiliar& r){
    RegistroA reg;

    this->term->write(r.getTermino());
    reg.o_Docs = (unsigned int) this->docs->tellp();

    vector<uint32_t>* docs = r.getDocumentos();
    reg.numDocs = (unsigned short) docs->size();

    this->o_PosAnterior = 0;

    for (unsigned int i = 0; i < docs->size(); i++) {
        uint32_t doc = docs->at(i);
        vector<uint32_t> p = r.getPosiciones(doc);
        sort(p.begin(), p.end());

        this->docs->addGammaCode((unsigned int) doc);
        this->docs->addGammaCode((unsigned int) p.size());
        this->docs->addDeltaCode((unsigned int) (((uint32_t)this->pos->tellp()) - this->o_PosAnterior));
        this->docs->rellenarBuffer();

        this->o_PosAnterior = (uint32_t) this->pos->tellp();

        this->pos->addDeltaCode((unsigned int) p.at(0));

        for (unsigned int j = 1; j < p.size(); j++){
            this->pos->addDeltaCode((unsigned int) p.at(j) - p.at(j-1));
        }this->pos->rellenarBuffer();p.clear();
    }

    Archivo::write((char*) &reg.o_Docs, sizeof(unsigned int));
    Archivo::write((char*) &reg.numDocs, sizeof(unsigned short));
    this->length = this->tellp();

    docs->clear();
}

int ArchivoAuxiliar::read(RegistroAuxiliar* r) {
    this->clear();
    unsigned int o_Docs;
    unsigned short numDocs;

    r->setTermino(this->term->read());
    Archivo::read((char*) &o_Docs, sizeof(unsigned int));
    Archivo::read((char*) &numDocs, sizeof(unsigned short));

    for (unsigned int i = 0; i < numDocs; i++) {
        unsigned int doc = (unsigned int) this->docs->getGammaCode();
        r->agregarDocumento((uint32_t) doc);
        uint16_t frecuencia = (uint16_t) this->docs->getGammaCode();
        this->docs->getDeltaCode();

        unsigned int posAnterior = this->pos->getDeltaCode();
        r->agregarPosicion(doc, posAnterior);

        for (unsigned int j = 1; j < frecuencia; j++) {
            int distancia = this->pos->getDeltaCode();
            r->agregarPosicion(doc, posAnterior + distancia);
            posAnterior += distancia;
        }
    }    

    if ((unsigned int)this->tellg() == length)
        return RES_EOF;

    return RES_OK;
}

void ArchivoAuxiliar::read(RegistroAuxiliar *r, unsigned int pos) {
    unsigned int o_Docs;
    unsigned short numDocs;

    this->clear();
    this->seekg(pos * (sizeof(unsigned int) + sizeof(unsigned short)), ios::beg);

    Archivo::read((char*) &o_Docs, sizeof(unsigned int));
    Archivo::read((char*) &numDocs, sizeof(unsigned short));

    this->docs->clear();
    this->docs->seekg(o_Docs, ios::beg);

    unsigned int doc = (unsigned int) this->docs->getGammaCode();
    uint16_t frecuencia = (uint16_t) this->docs->getGammaCode();
    this->o_PosAnterior = (uint32_t) this->docs->getDeltaCode();

    this->pos->clear();
    this->pos->seekg(this->o_PosAnterior, ios::beg);

    for (unsigned int i = 0; i < numDocs; i++) {
        r->agregarDocumento(doc);

        unsigned int posAnterior = this->pos->getDeltaCode();
        r->agregarPosicion(doc, posAnterior);

        for (unsigned int j = 1; j < frecuencia; j++) {
            unsigned int distancia = this->pos->getDeltaCode();
            r->agregarPosicion(doc, posAnterior + distancia);
            posAnterior += distancia;
        }

        if (i < (unsigned int) (numDocs - 1)) {
            doc = (unsigned int) this->docs->getGammaCode();
            frecuencia = (uint16_t) this->docs->getGammaCode();
            this->docs->getDeltaCode();
        }
    }
}

void ArchivoAuxiliar::rename(const char *filename) {
    this->update();

    std::rename((string(Archivo::indexDir)).append("IndiceInvertidoAux").c_str(), (string(Archivo::indexDir)).append(filename).c_str());
    std::rename((string(Archivo::indexDir)).append("DocumentosAux").c_str(), (string(Archivo::indexDir)).append("Documentos").c_str());
    std::rename((string(Archivo::indexDir)).append("PosicionesAux").c_str(), (string(Archivo::indexDir)).append("Posiciones").c_str());
    std::rename((string(Archivo::indexDir)).append("TerminosAux").c_str(), (string(Archivo::indexDir)).append("Terminos").c_str());
}

void ArchivoAuxiliar::update() {
    //this->regSig.numTerm = 0;

    this->docs->flush();
    this->pos->flush();
    this->term->flush();

    this->docs->clear();
    this->pos->clear();
    this->term->clear();

    this->docs->seekg(0, ios::beg);
    this->pos->seekg(0, ios::beg);
    this->term->seekg(0, ios::beg);
}

void ArchivoAuxiliar::destroy() {
    this->docs->destroy();
    this->pos->destroy();
    this->term->destroy();

    this->close();
    remove(this->filename);
}

vector<string>* ArchivoAuxiliar::getTerminos() {
    this->term->clear();
    this->term->seekg(0, ios::beg);

    string token = string("");
    vector<string>* vec = new vector<string>;

    while (true) {
        token = this->term->read();
        if (token.empty())
            break;

        vec->push_back(token);
    }

    return vec;
}

ArchivoAuxiliar::~ArchivoAuxiliar() {
    this->docs->close();
    this->pos->close();
    this->term->close();
}
